package com.chapter14.lianxi;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * https://zhuanlan.zhihu.com/p/299064490?utm_medium=social&utm_psn=1789626883522060288&utm_source=wechat_session
 */
public class demo1 {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 20,"male", "New York"));
        personList.add(new Person("Jack", 7000, 20,"male", "Washington"));
        personList.add(new Person("Lily", 7800, 20,"female", "Washington"));
        personList.add(new Person("Anni", 8200, 20,"female", "New York"));
        personList.add(new Person("Owen", 9500, 20,"male", "New York"));
        personList.add(new Person("Alisa", 7900,20, "female", "New York"));

        //1、筛选，是按照一定的规则校验流中的元素，将符合条件的元素提取到新的流中的操作。
        //筛选员工中工资高于8000的人，并形成新的集合。 形成新集合依赖collect（收集）  .map是提取姓名
        List<String> fiterList = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName)
                .collect(Collectors.toList());
        System.out.println("高于8000的员工姓名：" + fiterList);

        List<Person> fiterList2 = personList.stream().filter(x -> x.getSalary() > 8000).collect(Collectors.toList());
        List list = new ArrayList<>();
        for(Person person:fiterList2){
            list.add(person.getName());
        }
        System.out.println("高于8000的员工姓名：" +list);


        //2、聚合（max/min/count)
        //获取员工工资最高的人。
        //Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true，调用get()方法会返回该对象。
        Optional<Person> max = personList.stream().max(Comparator.comparingInt(Person::getSalary));
        System.out.println("员工工资最大值：" + max.get().getSalary());



        //3、映射(map/flatMap)
        //map：接收一个函数作为参数，该函数会被应用到每个元素上，并将其映射成一个新的元素。
        //flatMap：接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流。
        //将员工的薪资全部增加1000。
        List<Person> personListNew = personList.stream().map(person -> {
            person.setSalary(person.getSalary() + 10000);
            return person;
        }).collect(Collectors.toList());
        for(Person person:personListNew){
            System.out.println(person.getSalary());
        }



        // 4、归约(reduce)
        //求所有员工的工资之和和最高工资
        // 求工资之和方式1：
        Optional<Integer> sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);
        // 求工资之和方式2：
        Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(),
                (sum1, sum2) -> sum1 + sum2);
        // 求工资之和方式3：引用
        Integer sumSalary3 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);
        System.out.println("工资之和：" + sumSalary.get() );


        // 求最高工资方式1：
        Optional<Integer> maxSalary1 = personList.stream().map(Person::getSalary).reduce(Integer::max);
        // 求最高工资方式2：
        Integer maxSalary2 = personList.stream().reduce(0, (max1, p) -> max1 > p.getSalary() ? max1 : p.getSalary(),
                (max1, max2) -> max1 > max2 ? max1 : max2);
        // 求最高工资方式3：
        Integer maxSalary3 = personList.stream().reduce(0, (max1, p) -> max1 > p.getSalary() ? max1 : p.getSalary(),
                Integer::max);
        System.out.println("最高工资：" + maxSalary1.get());



        //5、收集(collect)
        //51 归集(toList/toSet/toMap)
        Map<?, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
                .collect(Collectors.toMap(Person::getName, p -> p));
        System.out.println("toMap:" + map);

        //5.2 统计(count/averaging)
        // 求总数
        Long count = personList.stream().collect(Collectors.counting());
        // 求平均工资
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        // 求最高工资
        Optional<Integer> max2 = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
        // 求工资之和
        Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
        // 一次性统计所有信息
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
        System.out.println("员工总数：" + count);
        System.out.println("员工平均工资：" + average);
        System.out.println("员工工资总和：" + sum);
        System.out.println("员工工资所有统计：" + collect);

        //5.3 分组(partitioningBy/groupingBy)
        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按性别分组
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 将员工先按性别分组，再按地区分组
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        System.out.println("员工按薪资是否大于8000分组情况：" + part);
        System.out.println("员工按性别分组情况：" + group);
        System.out.println("员工按性别、地区：" + group2);


        //5.4 接合(joining)
        // joining可以将stream中的元素用特定的连接符（没有的话，则直接连接）连接成一个字符串。
        String names = personList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
        System.out.println("所有员工的姓名：" + names);

        //5.5 归约(reducing)
        // 每个员工减去起征点后的薪资之和
        //Collectors类提供的reducing方法，相比于stream本身的reduce方法，增加了对自定义归约的支持。
        Integer sum2 = personList.stream().collect(Collectors.reducing(0, Person::getSalary, (i, j) -> (i + j - 5000)));
        System.out.println("员工扣税薪资总和：" + sum2);

        // stream的reduce
        Optional<Integer> sum3 = personList.stream().map(Person::getSalary).reduce(Integer::sum);
        System.out.println("员工薪资总和：" + sum3.get());


        //6. 排序(sorted)
        // 按工资升序排序（自然排序）
        List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
                .collect(Collectors.toList());
        // 按工资倒序排序
        List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄升序排序
        List<String> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
                .collect(Collectors.toList());
        // 先按工资再按年龄自定义排序（降序）
        List<String> newList4 = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());

        System.out.println("按工资升序排序：" + newList);
        System.out.println("按工资降序排序：" + newList2);
        System.out.println("先按工资再按年龄升序排序：" + newList3);
        System.out.println("先按工资再按年龄自定义降序排序：" + newList4);


        //7. 提取/组合  流也可以进行合并、去重、限制、跳过等操作。
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        // concat:合并两个流 distinct：去重
        List<String> new1 = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        // limit：限制从流中获得前n个数据
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip：跳过前n个数据
        List<Integer> collect3 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());

        System.out.println("流合并：" + new1);
        System.out.println("limit：" + collect2);
        System.out.println("skip：" + collect2);

    }
}
